// 链表 的 技巧 与 常用方法
// 1.画图 - 链表的题目大多数都是模拟题，画图能够使模拟过程更加清晰
// 2.引入虚拟头结点 newHead，目的是为了让尾指针具备一般性，一个循环搞定代码
// 3.不吝啬空间，敢于定义变量 prev, next等：prev - 前边链的尾指针  next - 后边链的头指针 有头有尾，不怕链断
// 4.快慢双指针 - 判断环，找环的入口，找链表中的倒数第 n 个节点
// 5.创建新节点
// 6.尾插法 - 定义尾节点 tail，维护尾节点
// 7.头插法 - 引入虚拟头结点 newHead: node -> newHead.next, newHead.next -> node
// 8.合并多个有序数组或者合并多个链表，见到“合并”两个字，必须要想起来分治的思想

// 例题 5：
// 给你链表的头节点 head ，每 k 个节点一组进行翻转，请你返回修改后的链表。
//
//        k 是一个正整数，它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍，那么请将最后剩余的节点保持原有顺序。
//
//        你不能只是单纯的改变节点内部的值，而是需要实际进行节点交换。
//
//        示例 1：
//
//        输入：head = [1,2,3,4,5], k = 2
//        输出：[2,1,4,3,5]
//        示例 2：
//
//        输入：head = [1,2,3,4,5], k = 3
//        输出：[3,2,1,4,5]
//
//        提示：
//        链表中的节点数目为 n
//        1 <= k <= n <= 5000
//        0 <= Node.val <= 1000
//
//        进阶：你可以设计一个只用 O(1) 额外内存空间的算法解决此问题吗？

// 解题思路：
// 先求出需要逆序多少组
// 重复 n 次，长度为 k 的链表的逆序
// 重点就是掌握好链表逆序 - 头插法
// 以及要保证找到链表的前驱 prev 和后继 next，防止链表断裂

public class ReverseKGroup {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode cur = head;
        int n = 0;
        while(cur != null) {
            cur = cur.next;
            n++;
        }

        ListNode newHead = new ListNode();
        ListNode prev = newHead;
        cur = head;
        ListNode next = cur.next;
        int m = n / k;
        while(m > 0){
            m--;
            int i = k;
            ListNode tail = cur;
            while(i > 0){
                i--;
                cur.next = prev.next;
                prev.next = cur;
                cur = next;
                if(cur != null) next = cur.next;
            }
            prev = tail;
        }
        prev.next = cur;
        return newHead.next;
    }
}
